package main

import (
	"fmt"
	"go-tour/concurency_pike/boring_function"
	"math/rand"
	"time"
)

func main() {
	//DemoBoring()
	//DemoChannelGenerator()
	//DemoHandlerOfService()
	DemoMultiplexingWithFanIn(fanIn)
	//DemoMultiplexingWithFanIn(fanInWithSelect)
	//DemoRestoreSequence()
}

func DemoBoring() {
	c := make(chan string)
	go boring_function.Boring("boring!", c)
	fmt.Println("I'm listening.")
	for i := 0; i < 5; i++ {
		fmt.Printf("You say: %q\n", <-c) // Receive expression is just a value.
	}
	fmt.Println("You're boring; I'm leaving.")
}

/*
https://talks.golang.org/2012/concurrency.slide#25
Generator: function that returns a channel
Channels are first-class values, just like strings or integers.
*/
func DemoChannelGenerator() {
	c := boring_function.ChannelGenerator("boring!")
	fmt.Println("I'm listening.")
	for i := 0; i < 5; i++ {
		fmt.Printf("You say: %q\n", <-c) // Receive expression is just a value.
	}
	fmt.Println("You're boring; I'm leaving.")
}

/*
https://talks.golang.org/2012/concurrency.slide#26
Channels as a handle on a service
Our boring function returns a channel that lets us communicate with the boring service it provides.

We can have more instances of the service.
*/
func DemoHandlerOfService() {
	joe := boring_function.ChannelGenerator("Joe")
	ann := boring_function.ChannelGenerator("Ann")
	for i := 0; i < 5; i++ {
		fmt.Println(<-joe)
		fmt.Println(<-ann)
	}
	fmt.Println("You're both boring; I'm leaving.")
}

type FanInFunctionType func(input1, input2 <-chan string) <-chan string

func fanInWithSelect(input1, input2 <-chan string) <-chan string {
	c := make(chan string)
	go func() {
		for {
			select {
			case s := <-input1:
				c <- s
			case s := <-input2:
				c <- s
			}
		}
	}()
	return c
}

func fanIn(input1, input2 <-chan string) <-chan string {
	c := make(chan string)
	/*
		go - 当一个 channel 使用两个箭头写入go中的另一个 channel 是什么意思
		https://www.coder.work/article/7187572
	*/
	go func() {
		for {
			c <- <-input1
		}
	}()
	go func() {
		for {
			c <- <-input2
		}
	}()
	return c
}

// DemoMultiplexingWithFanIn /*
func DemoMultiplexingWithFanIn(fanInFunc FanInFunctionType) {
	c := fanInFunc(boring_function.ChannelGenerator("Joe"), boring_function.ChannelGenerator("Ann"))
	for i := 0; i < 10; i++ {
		fmt.Println(<-c)
	}
	fmt.Println("You're both boring; I'm leaving.")
}

type Message struct {
	str  string
	wait chan bool
}

func fanInMessageOld(input1, input2 <-chan Message) <-chan Message {
	c := make(chan Message)
	/*
	 */
	go func() {
		for {
			c <- <-input1
		}
	}()
	go func() {
		for {
			c <- <-input2
		}
	}()
	return c
}

func fanInMessage(inputs ...<-chan Message) <-chan Message { // HL
	c := make(chan Message)
	for i := range inputs {
		input := inputs[i] // New instance of 'input' for each loop.
		go func() {
			for {
				c <- <-input
			}
		}()
	}
	return c
}

/*
https://talks.golang.org/2012/concurrency.slide#29
*/
func DemoRestoreSequence() {
	c := fanInMessage(ProduceMessage("Joe"), ProduceMessage("Ann"), ProduceMessage("Dav"))

	for i := 0; i < 5; i++ {
		msg1 := <-c
		fmt.Println(msg1.str)
		msg2 := <-c
		fmt.Println(msg2.str)
		msg3 := <-c
		fmt.Println(msg3.str)
		msg1.wait <- true
		msg2.wait <- true
		msg3.wait <- true
	}
}
func ProduceMessage(msg string) (c chan Message) {
	c = make(chan Message)
	waitForIt := make(chan bool) // Shared between all messages.
	go func() {
		for i := 0; ; i++ {
			c <- Message{fmt.Sprintf("%s: %d", msg, i), waitForIt}
			time.Sleep(time.Duration(rand.Intn(2e3)) * time.Millisecond)
			<-waitForIt
		}
	}()
	return
}
